package server;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import pojo.ChessOnlineRoom;
import pojo.UserAccount;
import transfer.*;
import util.Utils;
import view.ChessOnlineView;
import view.HomePageView;
import view.LeagueManageView;
import view.UserManageView;

public class Server implements Runnable {
	public static final String HOME_PAGE_VIEW_ID = "home-page-view";
	public static final String LEAGUE_MANAGE_VIEW_ID = "league-manage-view";
	public static final String USER_MANAGE_VIEW_ID = "user-manage-view";

	public JFrame frame;
	private ArrayList<Worker> listWorker = new ArrayList<Worker>();
	private ArrayList<ChessOnlineRoom> rooms = new ArrayList<ChessOnlineRoom>();
	private String host;
	private int port;

	private ServerSocket ss;
	private Socket client;
	//BufferedReader br;
	// data
	int currentTurn = -1;

	// View management
	private Map<String, ChessOnlineView> mViews = new HashMap<String, ChessOnlineView>();
	private HomePageView mHomePageView;
	private LeagueManageView mLeagueManageView;
	private UserManageView mUserManageView;
	private UserAccount acc = null;

	public UserAccount getAcc() {
		return acc;
	}

	public void setAcc(UserAccount acc) {
		this.acc = acc;
	}
	
	public int getCurrentTurn() {
		return currentTurn;
	}

	public void setCurrentTurn(int currentTurn) {
		this.currentTurn = currentTurn;
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Server window = new Server();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 * 
	 * @throws IOException
	 */
	public Server() throws IOException {
		getConfigFromXML();
		System.out.println("server host: " + host);
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 * 
	 * @throws IOException
	 */
	private void initialize() throws IOException {
		frame = new JFrame();
		// frame.setSize(new Dimension(800, 400));
		frame.setResizable(true);
		frame.setBounds(0, 50, 800, 600);
		frame.setTitle("Server");
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.getContentPane().setLayout(new BorderLayout());
		
	    frame.addWindowListener(new WindowAdapter() {
	        public void windowClosing(WindowEvent winEvt) {
	        	int chooseAnswer = JOptionPane.showConfirmDialog(null, "Do you really want to exit?", "Confirm", JOptionPane.YES_NO_OPTION);
	    		
	        	if (chooseAnswer == JOptionPane.YES_OPTION){
	    			sendObject(new StringData("Disconnect", null));
		            System.exit(0);
	    		}
	        }
	    });
		new Thread(this).start();
		
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("Start Server!");
		
		int count = 0;
		try {
			showHomePageView();
			SocketAddress sa= new InetSocketAddress(host, port);
			ss = new ServerSocket();
			ss.bind(sa);
			while (true) {
				client = ss.accept();
				Worker worker = new Worker(client, this, count);
				listWorker.add(worker);
				new Thread(worker).start();
				count++;
			}

		}
		catch (SocketException se){
			JOptionPane.showMessageDialog(frame, "Cannot bind to host, please stop and recheck Config.xml ");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void sendObject(Data data) {
		for (Worker worker : listWorker) {
			if(worker != null){
				worker.sendObject(data);
			}
			
		}
	}

	public void sendObject(Data data, int id) {
		for (Worker worker : listWorker) {
			if (worker != null && worker.getId() != id) {
				
					worker.sendObject(data);
				
			}
		}
	}

	public void sendObjectToAllInRoom(Data data, int idRoom) {
		ChessOnlineRoom room = rooms.get(idRoom);
		System.out.println("util room" + idRoom);
		if(room != null){
			listWorker.get(room.getmWorkerIdA()).sendObject(data);
			listWorker.get(room.getmWorkerIdB()).sendObject(data);
		}
	}

	public void sendObjectToAnotherInRoom(Data data, int idRoom, int idSender) {
		ChessOnlineRoom room = rooms.get(idRoom);
		if (idSender == room.getmWorkerIdA())
			listWorker.get(room.getmWorkerIdB()).sendObject(data);
		else
			listWorker.get(room.getmWorkerIdA()).sendObject(data);
	}

	public void sendObjectToId(Data data, int id) {
		listWorker.get(id).sendObject(data);
	}

	public void addRoom(ChessOnlineRoom room) {
		rooms.add(room);
	}

	public int getRoomIndex() {
		return rooms.size();
	}

	public void removeRoom(int roomId) {
		// rooms.remove(roomId);
		rooms.set(roomId, null);
	}

	public ArrayList<ChessOnlineRoom> getRooms() {
		return rooms;
	}

	public void setRooms(ArrayList<ChessOnlineRoom> rooms) {
		this.rooms = rooms;
	}
	
	private void removeView(String id, ChessOnlineView view) {
		this.mViews.remove(id);
		this.frame.remove(view);
		view = null;
	}
	
	private void addViewComponent(String id, ChessOnlineView component, String layout) {
		this.frame.getContentPane().add(component, layout);
		this.addView(id, component);
	}
	
	private void addView(String id, ChessOnlineView view) {
		if(!this.mViews.containsKey(id)) {
			this.mViews.put(id, view);
		}
	}
	
	public void showView(String id) {
		for(ChessOnlineView view : this.mViews.values()) {
			view.setVisible(false);
			view.onHide();
		}
		
		this.mViews.get(id).onShow();
		this.mViews.get(id).setVisible(true);
		this.frame.invalidate();
	}
	
	public void showHomePageView() {
		// Clear the SESSION
			Utils.removeUserSession();
		
		if(mHomePageView != null) {
			removeView(Server.HOME_PAGE_VIEW_ID, mHomePageView);
		}
		
		mHomePageView = new HomePageView(this);
		this.addViewComponent(Server.HOME_PAGE_VIEW_ID, mHomePageView, BorderLayout.CENTER);
		this.showView(Server.HOME_PAGE_VIEW_ID);
		
	}
	
	public void showLeagueManageView() {
		if(mLeagueManageView != null) {
			removeView(Server.LEAGUE_MANAGE_VIEW_ID, mLeagueManageView);
		}
		
		mLeagueManageView = new LeagueManageView(this);
		this.addViewComponent(Server.LEAGUE_MANAGE_VIEW_ID, mLeagueManageView, BorderLayout.CENTER);
		
		this.showView(LEAGUE_MANAGE_VIEW_ID);
	}
	
	public void showUserAccountView() {
		if(mUserManageView != null) {
			removeView(Server.USER_MANAGE_VIEW_ID, mUserManageView);
		}
		
		mUserManageView = new UserManageView(this);
		this.addViewComponent(Server.USER_MANAGE_VIEW_ID, mUserManageView, BorderLayout.CENTER);
		
		this.showView(USER_MANAGE_VIEW_ID);
	}
	public void removeWorker(int id){
		listWorker.set(id, null);
	}
	private void getConfigFromXML(){
		try {

			File fXmlFile = new File("Config.xml");
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();

			NodeList nList = doc.getElementsByTagName("config");

			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);

				if (nNode.getNodeType() == Node.ELEMENT_NODE) {

					Element eElement = (Element) nNode;

					
					host = eElement.getElementsByTagName("host")
							.item(0).getTextContent();

					port = Integer.parseInt(eElement.getElementsByTagName("port")
							.item(0).getTextContent());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
